﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;

namespace InForTheCloud.OAuthLite
{
    public class OAuthBaseStringBuilder
    {
        const string METHODS = "GET PUT POST DELETE";
        const string HTTP = "http://";
        const string HTTPS = "https://";
        const string HTTP_GET = "GET";
        const string OAUTH_CONSUMER_KEY = "oauth_consumer_key";
        const string OAUTH_TOKEN = "oauth_token";
        const string OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
        const string OAUTH_TIMESTAMP = "oauth_timestamp";
        const string OAUTH_NONCE = "oauth_nonce";
        const string OAUTH_SIGNATURE = "oauth_signature";
        const string HMAC_SHA1 = "HMAC-SHA1";
        const int HTTP_PORT = 80;
        const int HTTPS_PORT = 443;
        string _method = HTTP_GET;
        string _proto = HTTP;
        string _host = string.Empty;
        string _path = string.Empty;
        int _port = HTTP_PORT;
        List<KeyValuePair<string, string>> queryParameters = new List<KeyValuePair<string, string>>();
        NameValueCollection oauthParameters = new NameValueCollection();
        public OAuthBaseStringBuilder http()
        {
            
            return scheme (HTTP);
        }

        public OAuthBaseStringBuilder https()
        {
            return scheme (HTTPS);
        }

        public OAuthBaseStringBuilder get()
        {
            return method(HTTP_GET);
        }

        public OAuthBaseStringBuilder host(string host)
        {
            this._host = host.ToLowerInvariant();
            return this;
        }

        public OAuthBaseStringBuilder path(string path)
        {
            this._path = path;
            return this;
        }

        public OAuthBaseStringBuilder query(string name, string value)
        {
            if (name.ToLowerInvariant() == OAUTH_SIGNATURE)
                return this;
            queryParameters.Add(new KeyValuePair<string, string>(name.Encode(), value.Encode()));
            return this;
        }

        public OAuthBaseStringBuilder oauth_consumer_key(string value)
        {
            queryParameters.Add( new KeyValuePair<string, string>( OAUTH_CONSUMER_KEY, value.Encode ()));
            oauthParameters.Add(OAUTH_CONSUMER_KEY, value.Encode());
            return this;
        }

        public OAuthBaseStringBuilder oauth_token(string value)
        {
            queryParameters.Add( new KeyValuePair<string, string>( OAUTH_TOKEN, value.Encode()));
            oauthParameters.Add(OAUTH_TOKEN, value.Encode());
            return this;
        }

        public OAuthBaseStringBuilder oauth_signature_method_HMAC_SHA1()
        {
            queryParameters.Add(new KeyValuePair<string, string>( OAUTH_SIGNATURE_METHOD, HMAC_SHA1));
            oauthParameters.Add(OAUTH_SIGNATURE_METHOD, HMAC_SHA1);
            return this;
        }

        public OAuthBaseStringBuilder oauth_timestamp(int value)
        {
            queryParameters.Add(new KeyValuePair<string, string> ( OAUTH_TIMESTAMP, value.ToString()));
            oauthParameters.Add(OAUTH_TIMESTAMP, value.ToString());
            return this;
        }

        public OAuthBaseStringBuilder oauth_nonce(string value)
        {
            queryParameters.Add(new KeyValuePair<string, string>( OAUTH_NONCE, value.Encode()));
            oauthParameters.Add(OAUTH_NONCE, value.Encode());
            return this;
        }

        public string ToHeader(string signature)
        {
            StringBuilder headerBuilder = new StringBuilder("OAuth ")
                .AppendFormat("{0}=\"{1}\"", OAUTH_SIGNATURE, signature.Encode());

            foreach (string key in oauthParameters .Keys )
            {
                headerBuilder.AppendFormat(",{0}=\"{1}\"", key, oauthParameters[key]);
            }
            
            return headerBuilder.ToString();
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder(_method)
            .Append('&')
            .Append(_proto.Encode());


            
            if (_port != HTTP_PORT && _port != HTTPS_PORT )
            {
                builder.Append(string.Format ("{0}:{1}",_host, _port).Encode());
            } else 
            {
                builder.Append(_host.Encode());
            }

            builder.Append(_path.Encode())
            .Append('&');
            StringBuilder queryBuilder = new StringBuilder();
            queryParameters.Sort(new KeyValueComparer());

            if (queryParameters.Count > 0)
            {
                queryBuilder.AppendFormat("{0}={1}", queryParameters[0].Key, queryParameters[0].Value);
                for (int i = 1; i < queryParameters.Count; i++)
                {
                    queryBuilder.AppendFormat("&{0}={1}", queryParameters[i].Key, queryParameters[i].Value);
                }
            }

            builder.Append(queryBuilder.ToString().Encode());
            return builder.ToString();
         }
            
        class KeyValueComparer : IComparer <KeyValuePair<string , string>> {

            public int Compare(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
            {
                int result = x.Key.CompareTo(y.Key);
                if (result == 0)
                    return x.Value.CompareTo(y.Value);
                return result;
            }
        }


        public OAuthBaseStringBuilder method(string method)
        {
            if (string.IsNullOrEmpty(method))
                throw new ArgumentNullException("method");
            if (METHODS.IndexOf(method.ToUpperInvariant()) < 0)
                throw new ArgumentOutOfRangeException("method");
            _method = method.ToUpperInvariant();
            return this;
        }

        public OAuthBaseStringBuilder scheme(string scheme)
        {
            if (string.IsNullOrEmpty(scheme))
                throw new ArgumentNullException("scheme");
            if (!scheme.EndsWith("://"))
                scheme += "://";
            _proto = scheme;
            return this;
        }

        public OAuthBaseStringBuilder port(int port)
        {
            _port = port;
            return this;
        }
    }
}
